package com.lightsaber.trade.as.service.planning.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang.StringUtils;
import org.dom4j.Attribute;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springside.modules.orm.Page;

import com.lightsaber.trade.as.dao.category.CategoryNodeDao;
import com.lightsaber.trade.as.dao.planning.DisciplinePlanningItemDao;
import com.lightsaber.trade.as.dao.planning.Level2PlanningDao;
import com.lightsaber.trade.as.dao.programme.ProgrammeDao;
import com.lightsaber.trade.as.entity.category.Category;
import com.lightsaber.trade.as.entity.category.CategoryLeaf;
import com.lightsaber.trade.as.entity.category.CategoryNode;
import com.lightsaber.trade.as.entity.category.SourceType;
import com.lightsaber.trade.as.entity.email.EmailNotificationType;
import com.lightsaber.trade.as.entity.planning.Level1Planning;
import com.lightsaber.trade.as.entity.planning.Level2DisciplineStatus;
import com.lightsaber.trade.as.entity.planning.Level2Planning;
import com.lightsaber.trade.as.entity.planning.Planning;
import com.lightsaber.trade.as.entity.planning.PlanningLevel;
import com.lightsaber.trade.as.entity.planning.PlanningStatus;
import com.lightsaber.trade.as.entity.planning.ProgrammePlanningItem;
import com.lightsaber.trade.as.entity.programme.BaseProgramme;
import com.lightsaber.trade.as.entity.programme.OfferingCampus;
import com.lightsaber.trade.as.entity.programme.PlanProjectionType;
import com.lightsaber.trade.as.entity.programme.Programme;
import com.lightsaber.trade.as.entity.programme.ProgrammeCode;
import com.lightsaber.trade.as.entity.programme.ProgrammePlanProjectionType;
import com.lightsaber.trade.as.entity.programme.Stream;
import com.lightsaber.trade.as.entity.programme.SubjectGroup;
import com.lightsaber.trade.as.entity.programme.SubjectGroupItem;
import com.lightsaber.trade.as.service.category.CategoryNodeService;
import com.lightsaber.trade.as.service.email.EmailService;
import com.lightsaber.trade.as.service.param.ParamService;
import com.lightsaber.trade.as.service.planfirst.CategoryPlanningItemManager;
import com.lightsaber.trade.as.service.planning.Level2DisciplineStatusService;
import com.lightsaber.trade.as.service.planning.Level2PlanningService;
import com.lightsaber.trade.as.service.planning.PlanningService;
import com.lightsaber.trade.as.service.planning.ProgrammePlanningItemService;
import com.lightsaber.trade.as.service.programme.ProgrammeCodeManager;
import com.lightsaber.trade.as.service.programme.ProgrammeManager;
import com.lightsaber.trade.as.service.programme.ProgrammePlanProjectionTypeManager;
import com.lightsaber.trade.core.common.treegrid.GridDoc;
import com.lightsaber.trade.core.common.util.DateUtil;
import com.lightsaber.trade.core.dao.BaseDao;
import com.lightsaber.trade.core.service.BaseServiceImpl;

@Service
@Component
@Transactional
public class Level2PlanningServiceImpl extends BaseServiceImpl<Level2Planning, String> implements
        Level2PlanningService {

    private static final Logger log = LoggerFactory.getLogger(Level2PlanningServiceImpl.class);
    @Autowired
    private Level2PlanningDao level2PlanningDao;
    @Autowired
    private ProgrammeManager programmeManager;

    @Autowired
    private ProgrammeCodeManager codeManager;

    @Autowired
    ProgrammeDao programmeDao;
    @Autowired
    ParamService paramService;
    @Autowired
    private transient EmailService emailService;
    @Autowired
    private transient ProgrammePlanningItemService programmePlanningItemService;
    @Autowired
    private transient PlanningService planningService;
    @Autowired
    private CategoryNodeService categoryNodeService;
    @Autowired
    private CategoryPlanningItemManager categoryPlanningItemManager;

    @Autowired
    CategoryNodeDao categoryNodeDao;

    @Autowired
    DisciplinePlanningItemDao disciplinePlanningItemDao;
    @Autowired
    private Level2DisciplineStatusService level2DisciplineStatusService;

    @Autowired
    private transient ProgrammePlanProjectionTypeManager projectionTypeManager;

    @Override
    protected BaseDao<Level2Planning, String> getDao() {
        return level2PlanningDao;
    }

    @Override
    public void save(final Level2Planning level2Planning) {
        if (StringUtils.isBlank(level2Planning.getId())) {
            level2PlanningDao.save(level2Planning);
        } else {
            Level2Planning old = get(level2Planning.getId());
            old.setRemarks(level2Planning.getRemarks());
            // BeanUtils.copyProperties(level2Planning, old, new String[] {
            // BaseEntity.ID,
            // BaseEntity.VERSION, "planning", AuditedObject.CREATED_BY,
            // AuditedObject.CREATED_DATE,
            // "programmePlanningItems","planningExercise" });
            level2PlanningDao.update(old);
        }

    }

    // @Override
    // public Map<Integer, String> getAllPlanningExercise() {
    // List<Integer> yearList = level2PlanningDao.getAllPlanningExercise();
    // Map<Integer, String> plannerYearMap = new HashMap<Integer, String>();
    // plannerYearMap.put(0, "-All-");
    // for (Integer year : yearList) {
    // plannerYearMap.put(year, year + "/" + (year + 1));
    // }
    // return plannerYearMap;
    // }

    @Override
    public void findPage(final Level2Planning level2Planning, final Page<Level2Planning> page) {
        level2PlanningDao.findPage(level2Planning, page);
    }

    @Override
    public Level2Planning findById(final String id) {
        return level2PlanningDao.get(id);
    }

    // TODO
    @Override
    public String loadLevel2TreeByLevel1Status(final String level2PlanningId,
            final String programmeCode) {

        Level2Planning level2Planning = get(level2PlanningId);
        Category category = level2Planning.getPlanning().getCategory();
        Planning planning = planningService.findByCategoryIdAndPlanningLevel(category.getId(),
                PlanningLevel.FirstLevel);
        boolean isEndorsed = false;
        if (planning != null) {
            if (planning.getStatus().equals(PlanningStatus.Endorsed)) {
                isEndorsed = true;
            }
        }
        StringBuffer sb = new StringBuffer();
        sb.append("<Grid>");
        sb.append("<Cfg id='Root' MainCol='A' ColMoving='0' Dragging='0' Sorting='0' Alternate='0' />");
        // sb.append("<Cfg MaxVScroll='2000'/>");
        generateLevel2Cols(sb, isEndorsed);
        generateLevel2Head(sb, isEndorsed);
        generateLevel2Body(sb, isEndorsed, level2PlanningId, programmeCode, category);
        generateLevel2Bottom(sb);
        sb.append("</Grid>");
        log.info(" level2 tree :{}", sb.toString());
        return sb.toString();

    }

    private void generateLevel2Body(final StringBuffer sb, final boolean isEndorsed,
            final String level2PlanningId, final String programmeCode, final Category category) {
        sb.append("<Body><B>");
        if (isEndorsed) {
            if (StringUtils.isBlank(programmeCode)) {
                log.info(" generateLevel2Body programmeCode is null");
                generalProgrammeCategoryXML(null, category.getId(), sb, null, level2PlanningId);
                log.info(" generateLevel2Body end");
            } else {
                log.info(" generateLevel2Body programmeCode : ", programmeCode);
                List<CategoryNode> leafNodeList = new ArrayList<CategoryNode>();
                getLeafByProgrammeCode(null, category, programmeCode, null, leafNodeList);
                Map<String, CategoryNode> map = new HashMap<String, CategoryNode>();
                for (CategoryNode node : leafNodeList) {
                    map.put(node.getId(), node);
                    getNodes(node, map);
                }
                generalProgrammeCategoryXML(null, category.getId(), sb, null, level2PlanningId,
                        programmeCode, map);
                log.info(" generalProgrammeCategoryXML");
            }
        } else {
            getAllDisciplineGenerateXML(sb, level2PlanningId, programmeCode, category.getPlanYear());
        }
        sb.append("</B></Body>");
    }

    public void generalProgrammeCategoryXML(final String parentId, final String categoryId,
            final StringBuffer sb, final List<CategoryNode> children,
            final String level2PlanningId, final String programmeCode,
            final Map<String, CategoryNode> map) {
        if (StringUtils.isBlank(parentId)) {
            CategoryNode root = categoryNodeService.findByCategoryId(categoryId);
            sb.append("<I id='" + root.getId() + "' A='" + root.getName() + "' >");
            generalProgrammeCategoryXML(root.getId(), categoryId, sb, root.getChildren(),
                    level2PlanningId, programmeCode, map);
            sb.append("</I>");
        } else {
            if (!children.isEmpty()) {
                for (int i = 0; i < children.size(); i++) {
                    CategoryNode child = children.get(i);
                    if (child.getChildren().isEmpty()) {
                        if (map.containsKey(child.getId())) {
                            if (child.getIsLeafNode() == 1) {
                                String status = PlanningStatus.WaitingForSubmission.getLabel();
                                if (child.getLevel2status() != null) {
                                    status = child.getLevel2status().getLabel();
                                }
                                sb.append("<I id='"
                                        + child.getId()
                                        + "'  A=' "
                                        + child.getName()
                                        + "' programmePlan='&lt;input type=&#039;button&#039; class=&#039;dButton&#039; onclick=&#039;toViewDiscipline(null,&quot;"
                                        + level2PlanningId + "&quot;,&quot;" + child.getId()
                                        + "&quot;)&#039; value=&#039;View&#039; &gt;' status='"
                                        + status + "' />");
                            } else {
                                sb.append("<I id='" + child.getId() + "' A='" + child.getName()
                                        + "' />");
                            }
                        }
                    } else {
                        if (map.containsKey(child.getId())) {
                            // if (child.getIsDisciplineView() == 1) {
                            // sb.append("<I id='"
                            // + child.getId()
                            // + "'  A=' "
                            // + child.getName()
                            // +
                            // "' programmePlan='&lt;input type=&#039;button&#039; class=&#039;dButton&#039; onclick=&#039;toViewDiscipline(null,&quot;"
                            // + level2PlanningId + "&quot;," + isEndorsed +
                            // ",&quot;"
                            // + child.getId()
                            // +
                            // "&quot;)&#039; value=&#039;View&#039; &gt;' status='"
                            // + child.getLevel2status().getLabel() + "' >");
                            //
                            // } else {
                            sb.append("<I id='" + child.getId() + "' A='" + child.getName() + "' >");
                            // }
                            generalProgrammeCategoryXML(child.getId(), categoryId, sb,
                                    child.getChildren(), level2PlanningId, programmeCode, map);
                            sb.append("</I>");
                        } else {
                            generalProgrammeCategoryXML(child.getId(), categoryId, sb,
                                    child.getChildren(), level2PlanningId, programmeCode, map);
                        }
                    }
                }
            }
        }
    }

    private void getNodes(final CategoryNode leafNode, final Map<String, CategoryNode> map) {
        CategoryNode parent = leafNode.getParent();

        if (!map.containsKey(parent.getId())) {
            map.put(parent.getId(), parent);

        } else {
            return;
        }
        if (parent.getParent() == null) {
            return;
        }
        getNodes(parent, map);
    }

    private void getLeafByProgrammeCode(final String parentId, final Category category,
            final String programmeCode, final List<CategoryNode> children,
            final List<CategoryNode> leafNodeList) {
        if (StringUtils.isBlank(parentId)) {
            CategoryNode root = categoryNodeService.findByCategoryId(category.getId());
            getLeafByProgrammeCode(root.getId(), category, programmeCode, root.getChildren(),
                    leafNodeList);
        } else {
            if (!children.isEmpty()) {
                for (int i = 0; i < children.size(); i++) {
                    CategoryNode child = children.get(i);
                    if (child.getChildren().isEmpty()) {
                        if (child.getIsLeafNode() == 1) {
                            boolean includeCode = programmeDao
                                    .checkLeafAssociatedProgrammeByProgrammeCode(category,
                                            (CategoryLeaf) child, programmeCode);
                            if (includeCode) {
                                leafNodeList.add(child);
                            }
                        }
                    } else {
                        getLeafByProgrammeCode(child.getId(), category, programmeCode,
                                child.getChildren(), leafNodeList);
                    }
                }
            }
        }
    }

    public void generalProgrammeCategoryXML(final String parentId, final String categoryId,
            final StringBuffer sb, final List<CategoryNode> children, final String level2PlanningId) {
        if (StringUtils.isBlank(parentId)) {
            CategoryNode root = categoryNodeService.findByCategoryId(categoryId);
            // if (root.getIsDisciplineView() == 1) {
            // sb.append("<I id='"
            // + root.getId()
            // + "'  A=' "
            // + root.getName()
            // +
            // "' programmePlan='&lt;input type=&#039;button&#039; class=&#039;dButton&#039; onclick=&#039;toViewDiscipline(null,&quot;"
            // + level2PlanningId + "&quot;," + isEndorsed + ",&quot;" +
            // root.getId()
            // + "&quot;)&#039; value=&#039;View&#039; &gt;' status='"
            // + root.getLevel2status().getLabel() + "' >");
            //
            // } else {
            sb.append("<I id='" + root.getId() + "' A='" + root.getName() + "' >");
            // }
            generalProgrammeCategoryXML(root.getId(), categoryId, sb, root.getChildren(),
                    level2PlanningId);
            sb.append("</I>");

        } else {
            if (!children.isEmpty()) {
                for (int i = 0; i < children.size(); i++) {
                    CategoryNode child = children.get(i);
                    if (child.getChildren().isEmpty()) {
                        if (child.getIsLeafNode() == 1) {
                            String status = PlanningStatus.WaitingForSubmission.getLabel();
                            if (child.getLevel2status() != null) {
                                status = child.getLevel2status().getLabel();
                            }
                            sb.append("<I id='"
                                    + child.getId()
                                    + "'  A=' "
                                    + child.getName()
                                    + "' programmePlan='&lt;input type=&#039;button&#039; class=&#039;dButton&#039; onclick=&#039;toViewDiscipline(null,&quot;"
                                    + level2PlanningId + "&quot;,&quot;" + child.getId()
                                    + "&quot;)&#039; value=&#039;View&#039; &gt;' status='"
                                    + status + "' />");
                        } else {
                            sb.append("<I id='" + child.getId() + "' A='" + child.getName()
                                    + "' />");
                        }

                    } else {
                        // if (child.getIsDisciplineView() == 1) {
                        // sb.append("<I id='"
                        // + child.getId()
                        // + "'  A=' "
                        // + child.getName()
                        // +
                        // "' programmePlan='&lt;input type=&#039;button&#039; class=&#039;dButton&#039; onclick=&#039;toViewDiscipline(null,&quot;"
                        // + level2PlanningId + "&quot;," + isEndorsed +
                        // ",&quot;"
                        // + child.getId()
                        // +
                        // "&quot;)&#039; value=&#039;View&#039; &gt;' status='"
                        // + child.getLevel2status().getLabel() + "' >");
                        //
                        // } else {
                        sb.append("<I id='" + child.getId() + "' A='" + child.getName() + "' >");
                        // }
                        generalProgrammeCategoryXML(child.getId(), categoryId, sb,
                                child.getChildren(), level2PlanningId);
                        sb.append("</I>");
                    }
                }
            }
        }
    }

    private void getAllDisciplineGenerateXML(final StringBuffer sb, final String level2PlanningId,
            final String programmeCode, final int exerciseYear) {
        List<String> disciplineList = new ArrayList<String>();
        if (StringUtils.isBlank(programmeCode)) {
            disciplineList = programmeManager.getAllDiscipline(exerciseYear, null, null, null);
        } else {
            disciplineList = programmeManager.getDistinctDisciplineByCode(exerciseYear,
                    programmeCode);
        }
        sb.append("<I id='rootNode' A='Root' programmePlan=''>");
        for (String discipline : disciplineList) {
            sb.append("<I id='"
                    + discipline
                    + "' A='"
                    + paramService.getDisciplineLabel(discipline)
                    + "'  programmePlan='&lt;input type=&#039;button&#039; class=&#039;dButton&#039; onclick=&#039;toViewDiscipline(&quot;"
                    + discipline + "&quot;,&quot;" + level2PlanningId
                    + "&quot;,null)&#039; value=&#039;View&#039; &gt;'  />");
        }
        sb.append("</I>");
    }

    private void generateLevel2Cols(final StringBuffer sb, final boolean isEndorsed) {
        sb.append("<Cols>");
        sb.append("<C Name='A' Width='150' Type='Text' CanEdit='0' AVisible='0' />");
        sb.append("<C Name='programmePlan' Width='120' Type='Html' CanEdit='0' programmePlanVisible='0' Align='Center' />");
        if (isEndorsed) {
            sb.append("<C Name='status' Width='130' Type='Text' CanEdit='0' statusVisible='0' />");
        }
        sb.append("</Cols>");

        log.info("generateLevel2Cols");
    }

    private void generateLevel2Head(final StringBuffer sb, final boolean isEndorsed) {
        sb.append("<Head>");
        if (isEndorsed) {
            sb.append("<Header id='Header' A='Programme Category' programmePlan='Programme Plan' status='Status'/>");
        } else {
            sb.append("<Header id='Header' A='Discipline' AType='Html' programmePlan='Programme Plan' programmePlanAlign='Center' programmePlanType='Html' />");
        }
        sb.append("</Head>");
        log.info("generateLevel2Head");
    }

    private void generateLevel2Bottom(final StringBuffer sb) {
        sb.append("<Toolbar Visible='0' Styles='0' Reload='0' Print='0' Add='0' AddChild='0' Sort='0' Calc='0' ExpandAll='0' CollapseAll='0' Columns='0' Help='0' Debug='3' />");
        sb.append("<MenuCfg ShowDeleted='0' AutoSort='0' AutoUpdate='0' Separator1='0' MouseHover='0' ShowDrag='0' ShowPanel='0' />");
        sb.append("<Panel Copy='0' Move='0' Select='0' Delete='0' />");
        log.info("generateLevel2Bottom");
    }

    // /////////////////////////////////////////////////////////////////////////////

    // view discipline
    // TODO
    // /////////////////////////////////////////////////////////////////////////////

    @Override
    public String getViewDetail(final String disciplineType, final String level2PlanningId,
            final String categoryNodeId, final String searchByInService,
            final boolean isForEndorsement) {
        StringBuffer gridDoc = new StringBuffer();
        gridDoc.append("<Grid><Cfg id='Level2Planning' MainCol='A' ColMoving='0' Dragging='0' Sorting='0' Alternate='0' MaxHeight='1'/>");

        Level2Planning level2Planning = get(level2PlanningId);

        Category category = level2Planning.getPlanning().getCategory();

        Planning planning = planningService.findByCategoryIdAndPlanningLevel(category.getId(),
                PlanningLevel.FirstLevel);
        boolean isEndorsed = false;
        if (planning != null) {
            if (planning.getStatus().equals(PlanningStatus.Endorsed)) {
                isEndorsed = true;
            }
        }

        int planYear = category.getPlanYear();
        int level2Start = category.getLevel2Start();
        int level2End = category.getLevel2End();
        int studyYear = generateViewBody(gridDoc, level2Planning, disciplineType, isEndorsed,
                categoryNodeId, category, searchByInService, planYear, isForEndorsement,
                planning.getLevel1Planning());

        Map<String, String> bureauMap = new HashMap<String, String>();
        Map<String, String> fundingModeMap = new HashMap<String, String>();
        if (isEndorsed || isForEndorsement) {
            CategoryNode currentNode = categoryNodeService.findById(categoryNodeId);
            categoryPlanningItemManager.getBureauAndFundingMode(bureauMap, fundingModeMap,
                    currentNode);
        }
        generateViewLeftCols(gridDoc, isEndorsed, isForEndorsement);
        generateViewCols(gridDoc, studyYear, level2Start, level2End, planYear);
        generateViewDef(gridDoc, level2Start, level2End, studyYear, planYear, fundingModeMap);
        generateHead(gridDoc, planYear, level2Start, level2End, isEndorsed, studyYear,
                isForEndorsement);

        generateFoot(gridDoc, studyYear, level2Start, level2End, planYear, fundingModeMap);

        StringBuffer sb = new StringBuffer();
        sb.append("<Solid>");
        sb.append("<Search id='Level2Planning' Space='0' Cells='Expression,Mark,Clear' />");
        sb.append("</Solid>");
        gridDoc.append(sb.toString());
        gridDoc.append("<Toolbar Visible='0'/>");
        gridDoc.append("<Panel Copy='0' Move='0' Select='0' Delete='0'/></Grid>");

        return gridDoc.toString();

    }

    // One
    private int generateViewBody(final StringBuffer gridDoc, final Level2Planning level2Planning,
            final String disciplineType, final boolean isEndorsed, final String categoryNodeId,
            final Category category, final String searchByInService, final Integer exerciseYear,
            final boolean isForEndorsement, final Level1Planning level1Planning) {
        StringBuffer sb = new StringBuffer();

        sb.append("<Body><B>");
        int studyYear = 1;
        if (isEndorsed || isForEndorsement) {
            Map<String, Integer> map = new HashMap<String, Integer>();
            map.put("studyYear", studyYear);
            generalCurrentNodeXML(categoryNodeId, sb, category, null, level2Planning,
                    searchByInService, exerciseYear, map, isForEndorsement, level1Planning);
            studyYear = map.get("studyYear");
        } else {
            studyYear = generateDisciplineDetailTree(level2Planning, sb, disciplineType,
                    searchByInService, exerciseYear);
        }
        sb.append("</B></Body>");
        log.info("body : {}", sb.toString());
        gridDoc.append(sb.toString());
        return studyYear;
    }

    public void generalCurrentNodeXML(final String currentNodeId, final StringBuffer sb,
            final Category category, final List<CategoryNode> children,
            final Level2Planning level2Planning, final String searchByInService,
            final Integer exerciseYear, final Map<String, Integer> map,
            final boolean isForEndorsement, final Level1Planning level1Planning) {
        if (StringUtils.isNotBlank(currentNodeId)) {
            CategoryNode currentNode = categoryNodeService.findById(currentNodeId);
            if (currentNode.getIsLeafNode() == 1) {
                int year = generateCategoryNodeDetailTree(level2Planning, sb,
                        (CategoryLeaf) currentNode, category, searchByInService, exerciseYear,
                        isForEndorsement, level1Planning);
                map.put("studyYear", year);
            } else {
                sb.append("<I  Def='Tree' id='" + currentNode.getId() + "' A='"
                        + currentNode.getName() + "' >");
                generalCurrentNodeXML(null, sb, category, currentNode.getChildren(),
                        level2Planning, searchByInService, exerciseYear, map, isForEndorsement,
                        level1Planning);
                sb.append("</I>");
            }
        } else {
            if (!children.isEmpty()) {
                for (int i = 0; i < children.size(); i++) {
                    CategoryNode child = children.get(i);
                    if (child.getChildren().isEmpty()) {
                        if (child.getIsLeafNode() == 1) {
                            int year = generateCategoryNodeDetailTree(level2Planning, sb,
                                    (CategoryLeaf) child, category, searchByInService,
                                    exerciseYear, isForEndorsement, level1Planning);
                            int studyYear = map.get("studyYear");
                            if (year > studyYear) {
                                map.put("studyYear", year);
                            }
                        }
                    } else {
                        sb.append("<I  Def='Tree' id='" + child.getId() + "' A='" + child.getName()
                                + "'>");
                        generalCurrentNodeXML(null, sb, category, child.getChildren(),
                                level2Planning, searchByInService, exerciseYear, map,
                                isForEndorsement, level1Planning);
                        sb.append("</I>");
                    }
                }
            }
        }
    }

    private int generateCategoryNodeDetailTree(final Level2Planning level2Planning,
            final StringBuffer sb, final CategoryLeaf node, final Category category,
            final String searchByInService, final Integer exerciseYear,
            final boolean isForEndorsement, final Level1Planning level1Planning) {

        boolean sourceType = false;
        if (node.getSourceType().equals(SourceType.ManualInput)) {
            sourceType = true;
        }
        List<Programme> programmeList = programmeDao.getLeafAssociatedProgrammeByInService(
                category, node, searchByInService);
        // ?
        // List<Programme> programmeList = filterProgramme(exerciseYear,
        // codeList, null,
        // searchByInService);
        programmeList = filterTreeProgramme(exerciseYear, programmeList);

        sb.append("<I  Def='Tree' id='" + node.getId() + "' A='" + node.getName() + "'>");

        Map<String, List<Programme>> disciplineMap = getProgrammeListByType(programmeList,
                "discipline");
        int studyYear = 1;
        for (String param_D : disciplineMap.keySet()) {
            List<Programme> listByDiscipline = disciplineMap.get(param_D);

            Map<String, List<Programme>> studyModeMap = getProgrammeListByType(listByDiscipline,
                    "studyMode");

            String str = "";
            String dis = paramService.getDisciplineLabel(param_D);
            if (isForEndorsement) {
                str = generateLevel2DisciplineStatus(level2Planning, param_D, node);
                sb.append("<I  Def='Tree' id='" + level2Planning.getId() + "_" + node.getId() + "_"
                        + param_D + "_reject" + "' A='" + dis + "' " + str + ">");
            } else {
                log.info("level1Planning id : {}", level1Planning.getId());
                log.info("node id : {}", node.getId());
                log.info("exerciseYear : {}", exerciseYear);
                log.info("discipline : {}", param_D);
                long total = disciplinePlanningItemDao
                        .totalPlanedPlacesByLevel1AndNodeAndYearAndDiscipline(level1Planning, node,
                                exerciseYear, param_D);
                sb.append("<I  Def='Tree' id='" + param_D + "' A='" + dis + "' G='" + total + "' >");
            }

            for (String param_S : studyModeMap.keySet()) {
                List<Programme> listByStudyMode = studyModeMap.get(param_S);

                Map<String, List<Programme>> fundingModeMap = getProgrammeListByType(
                        listByStudyMode, "fundingMode");
                String stu = paramService.getStudyModeLabel(param_S);
                sb.append("<I  Def='Tree' id='" + param_S + "' A='" + stu + "'>");

                for (String param_F : fundingModeMap.keySet()) {
                    List<Programme> listByFundingMode = fundingModeMap.get(param_F);
                    if (listByFundingMode == null || listByFundingMode.isEmpty()) {
                        continue;
                    }
                    String fun = paramService.getFundingModeLabel(param_F);
                    sb.append("<I  Def='Tree' id='" + param_F + "' A='" + fun + "'>");

                    int year = generateDetailTree(level2Planning, sb, listByFundingMode,
                            exerciseYear, sourceType);
                    if (year > studyYear) {
                        studyYear = year;
                    }
                    sb.append("</I>"); // f
                }
                sb.append("</I>");// s
            }
            sb.append("</I>");// d
        }
        sb.append("</I>");// node
        return studyYear;

    }

    private String generateLevel2DisciplineStatus(final Level2Planning level2Planning,
            final String discipline, final CategoryNode categoryNode) {

        Level2DisciplineStatus l2s = level2DisciplineStatusService.findDisciplineStatus(
                level2Planning.getId(), discipline, categoryNode.getId());

        String str = "";
        if (l2s == null) {
            Level2DisciplineStatus newDS = new Level2DisciplineStatus();
            newDS.setDiscipline(discipline);
            newDS.setStatus(PlanningStatus.WaitingForEndorsement);
            newDS.setLevel2Planning(level2Planning);
            newDS.setReason("");
            newDS.setCategoryNode(categoryNode);
            level2DisciplineStatusService.save(newDS);
            str = " G='" + PlanningStatus.WaitingForEndorsement.getLabel()
                    + "' H='0' HCanEdit='1' KCanEdit='1' ";
        } else {
            String reason = l2s.getReason();
            if (reason == null) {
                reason = "";
            }
            if (l2s.getStatus().equals(PlanningStatus.Rejected)) {
                str = " G='" + l2s.getStatus().getLabel() + "' H='1' HCanEdit='0' K='" + reason
                        + "'  KCanEdit='0' ";
            } else {
                str = " G='" + l2s.getStatus().getLabel() + "' H='0' HCanEdit='1' K='" + reason
                        + "'  KCanEdit='1' ";
            }
        }
        return str;
    }

    private Map<String, List<Programme>> getProgrammeListByType(
            final List<Programme> programmeList, final String type) {
        Map<String, List<Programme>> map = new HashMap<String, List<Programme>>();
        for (Programme programme : programmeList) {
            String str = "";
            if (type.equals("discipline")) {
                str = programme.getDiscipline();
            } else if (type.equals("studyMode")) {
                str = programme.getStudyMode();
            } else if (type.equals("fundingMode")) {
                str = programme.getFundingMode();
            }

            if (map.containsKey(str)) {
                map.get(str).add(programme);
            } else {
                List<Programme> list = new ArrayList<Programme>();
                list.add(programme);
                map.put(str, list);
            }
        }
        return map;
    }

    // Two TODO
    private int generateDisciplineDetailTree(final Level2Planning level2Planning,
            final StringBuffer sb, final String disciplineType, final String searchByInService,
            final Integer exerciseYear) {

        log.info("generateDisciplineDetailTree : {}", disciplineType + " " + searchByInService
                + " " + exerciseYear);
        List<String> codeList = programmeManager.findByDiscipline(disciplineType,
                searchByInService, exerciseYear);
        List<Programme> progList = filterProgramme(exerciseYear, codeList, disciplineType,
                searchByInService);

        sb.append("<I  Def='Tree' id='" + disciplineType + "' A='"
                + paramService.getDisciplineLabel(disciplineType) + "'>");
        int studyYear = generateDetailTree(level2Planning, sb, progList, exerciseYear, false);
        sb.append("</I>");
        return studyYear;
    }

    private int generateDetailTree(final Level2Planning level2Planning, final StringBuffer sb,
            final List<Programme> programmeList, final Integer exerciseYear,
            final boolean sourceType) {
        int studyYear = 1;
        for (Programme programme : programmeList) {
            if (programme.getDuration() > studyYear) {
                studyYear = programme.getDuration();
            }
            if (programme.isRequireStream()) {
                List<Stream> streamList = programme.getStreams();
                for (Stream stream : streamList) {
                    loopCampus(level2Planning, sb, exerciseYear, sourceType, stream,
                            stream.getOfferingCampusList(), true, programme.getDuration(),
                            programme.getCode(), stream.getStreamCode());
                }
            } else {
                loopCampus(level2Planning, sb, exerciseYear, sourceType, programme,
                        programme.getOfferingCampusList(), false, programme.getDuration(),
                        programme.getCode(), "");
            }

        }
        return studyYear;
    }

    private void loopCampus(final Level2Planning level2Planning, final StringBuffer sb,
            final Integer exerciseYear, final boolean sourceType, final BaseProgramme programme,
            final List<OfferingCampus> campusList, final boolean isRequireStream,
            final int duration, final String progCode, final String streamCode) {
        for (OfferingCampus campus : campusList) {

            Map<String, String> map = generateYearData(level2Planning, progCode + streamCode,
                    duration, exerciseYear, campus.getOperatingCampus(), campus.getOperatingUnit(),
                    sourceType);
            String stNew = map.get("New");
            String stProg = map.get("Prog");
            String stBreak = map.get("Break");

            String oc = paramService.getCampusLabel(campus.getOperatingCampus());
            String ou = paramService.getDepartmentLabel(campus.getOperatingUnit(),
                    campus.getOperatingCampus());
            if (ou == null) {
                ou = "";
            }
            // campus row
            String i_id = "";
            if (isRequireStream) {
                sb.append("<I  Def='Tree' id='" + level2Planning.getId() + "_" + programme.getId()
                        + "' A='" + progCode + " (" + streamCode + ")' B='"
                        + programme.getTitleShort() + "' C='" + duration + "' D='" + oc + "' E='"
                        + ou + "' " + stBreak + ">");
                i_id = level2Planning.getId() + "_" + progCode + streamCode + "_"
                        + campus.getOperatingCampus() + "_" + campus.getOperatingUnit();
            } else {
                sb.append("<I  Def='Tree' id='" + level2Planning.getId() + "_" + programme.getId()
                        + "' A='" + progCode + "' B='" + programme.getTitleShort() + "' C='"
                        + duration + "' D='" + oc + "' E='" + ou + "' " + stBreak + ">");
                i_id = level2Planning.getId() + "_" + programme.getCode() + "_"
                        + campus.getOperatingCampus() + "_" + campus.getOperatingUnit();
            }
            // campus row
            // |
            // --> New

            sb.append("<I Def='Leaf' id='" + i_id + "_New" + "' A='New' F='New' " + stNew + "/>");
            // campus row
            // |
            // --> Prog.
            sb.append("<I Def='Leaf' id='" + i_id + "_Prog" + "' A='Prog.' F='Prog.' " + stProg
                    + "/>");

            sb.append("</I>");
        }
    }

    // Three
    private Map<String, String> generateYearData(final Level2Planning level2Planning,
            final String code, final int studyYear, final Integer exerciseYear,
            final String operatingCampus, final String operatingUnit, final boolean sourceType) {

        StringBuffer sbNew = new StringBuffer();
        StringBuffer sbProg = new StringBuffer();
        StringBuffer sbBreak = new StringBuffer();

        Map<String, String> map = new HashMap<String, String>();

        Integer level2Start = level2Planning.getPlanning().getCategory().getLevel2Start();
        Integer level2End = level2Planning.getPlanning().getCategory().getLevel2End();

        for (Integer year = level2Start; year < level2End + 1; year++) {
            if (year < exerciseYear) {
                for (int i = 1; i <= studyYear; i++) {
                    ProgrammePlanningItem ppi = generateViewDBData(level2Planning, code, year, i,
                            operatingCampus, operatingUnit);
                    String prefix = "_" + year + "_" + i;

                    String canEdit = prefix + "_actualCanEdit='0'  ";
                    if (sourceType) {
                        canEdit = prefix + "_actualCanEdit='1'  ";
                    }
                    sbNew.append(prefix + "='" + ppi.getPlannedPlacesNew() + "' ");
                    sbNew.append(prefix + "_actual='" + ppi.getPlannedPlacesNewActual() + "' "
                            + canEdit);

                    sbProg.append(prefix + "='" + ppi.getPlannedPlacesProg() + "' ");
                    sbProg.append(prefix + "_actual='" + ppi.getPlannedPlacesProgActual() + "' "
                            + canEdit);

                }
            } else if (year.equals(exerciseYear)) {
                for (int i = 1; i <= studyYear; i++) {
                    ProgrammePlanningItem ppi = generateViewDBData(level2Planning, code, year, i,
                            operatingCampus, operatingUnit);
                    String prefix = "_" + year + "_" + i;

                    String canEdit = prefix + "_actualCanEdit='0'  ";
                    if (sourceType) {
                        canEdit = prefix + "_actualCanEdit='1'  ";
                    }

                    sbNew.append(prefix + "='" + ppi.getPlannedPlacesNew() + "' ");
                    sbNew.append(prefix + "_actual='" + ppi.getPlannedPlacesNewActual() + "' "
                            + canEdit);

                    sbProg.append(prefix + "='" + ppi.getPlannedPlacesProg() + "' ");
                    sbProg.append(prefix + "_actual='" + ppi.getPlannedPlacesProgActual() + "' "
                            + canEdit);

                    sbBreak.append(prefix
                            + "_break='&lt;input type=&#039;button&#039; class=&#039;dButton&#039; onclick=&#039;toBreakDown(event,&quot;"
                            + ppi.getId() + "&quot;)&#039; value=&#039;Edit&#039; &gt;' ");
                }
            } else {
                for (int i = 1; i <= studyYear; i++) {
                    ProgrammePlanningItem ppi = generateViewDBData(level2Planning, code, year, i,
                            operatingCampus, operatingUnit);
                    String prefix = "_" + year + "_" + i;
                    sbNew.append(prefix + "='" + ppi.getPlannedPlacesNew() + "' ");
                    sbProg.append(prefix + "='" + ppi.getPlannedPlacesProg() + "' ");
                    sbBreak.append(prefix
                            + "_break='&lt;input type=&#039;button&#039; class=&#039;dButton&#039; onclick=&#039;toBreakDown(event,&quot;"
                            + ppi.getId() + "&quot;)&#039; value=&#039;Edit&#039; &gt;' ");
                }
            }
        }
        map.put("New", sbNew.toString());
        map.put("Prog", sbProg.toString());
        map.put("Break", sbBreak.toString());
        return map;
    }

    // Four
    private ProgrammePlanningItem generateViewDBData(final Level2Planning level2Planning,
            final String code, final int planYear, final int studyYear,
            final String operatingCampus, final String operatingUnit) {

        ProgrammePlanningItem ppi = programmePlanningItemService.findUniquePpi(
                level2Planning.getId(), code, planYear, studyYear, operatingCampus, operatingUnit);

        if (ppi == null) {
            ppi = new ProgrammePlanningItem();
            ppi.setLevel2Planning(level2Planning);

            ppi.setCode(code);
            ppi.setOperatingCampus(operatingCampus);
            ppi.setOperatingUnit(operatingUnit);

            ppi.setPlanYear(planYear);
            ppi.setStudyYear(studyYear);

            ppi.setPlannedPlacesNew(0);
            ppi.setPlannedPlacesProg(0);
            ppi.setPlannedPlacesNewActual(0);
            ppi.setPlannedPlacesProgActual(0);

            programmePlanningItemService.save(ppi);
        }

        return ppi;
    }

    private void generateHead(final StringBuffer gridDoc, final Integer planYear,
            final Integer level2Start, final Integer level2End, final boolean isEndorsed,
            final int studyYear, final boolean isForEndorsement) {

        StringBuffer header1 = new StringBuffer();
        StringBuffer header2 = new StringBuffer();

        int span = 6;
        String Aname = "Discipline/Prog. No.";
        String G = "";
        if (isForEndorsement) {
            span = 9;
            Aname = "Category/Discipline/Mode/Funding/Prog.No.";
            G = "G='Status' GAlign='Center' GType='Html' "
                    + "H='Reject' HAlign='Center' HType='Html' "
                    + "K='Reason' KAlign='Center' KType='Html' ";
        } else if (isEndorsed) {
            span = 7;
            Aname = "Category/Discipline/Mode/Funding/Prog.No.";
            G = "G='Target' GAlign='Center' GType='Html'";
        }

        header1.append("<Header Spanned='1' ASpan='" + span + "' A='Planning Exercise : "
                + planYear + "/" + DateUtil.formatYear(planYear + 1, "yy") + "' AType='Html' ");

        header2.append("<Header id='Header' A='" + Aname + "' AAlign='Left' AType='Html' "
                + "B='Programme Title' BAlign='Center' BType='Html' "
                + "C='Dura. (Yr.)' CAlign='Center' CType='Html' "
                + "D='Campus' DAlign='Center' DType='Html' "
                + "E='Dept' EAlign='Center' EType='Html'   FVisible='0'  " + G);

        for (Integer i = level2Start; i < level2End + 1; i++) {
            String prefix = "_" + i + "_";
            String headName = "Planned " + i + "-" + (i + 1);
            if (i < planYear) {
                header1.append(prefix + "1Span='" + (studyYear * 2 + 1) + "' " + prefix + "1='"
                        + headName + "' " + prefix + "1Align='Center' " + prefix + "1Type='Html' ");
                for (int j = 1; j <= studyYear; j++) {
                    String stu_prefix = prefix + j;
                    header2.append(stu_prefix + "='Yr-" + j + "' " + stu_prefix + "Align='Center' "
                            + stu_prefix + "Type='Html' ");
                    header2.append(stu_prefix + "_actual='Actual&lt;br/&gt;Enrollment' "
                            + stu_prefix + "_actualAlign='Center' " + stu_prefix
                            + "_actualType='Html' ");
                }
                header2.append(prefix + (studyYear * 2 + 1) + "='Total' " + prefix
                        + (studyYear * 2 + 1) + "Align='Center' " + prefix + (studyYear * 2 + 1)
                        + "Type='Html' ");
            } else if (i.equals(planYear)) {
                header1.append(prefix + "1Span='" + (studyYear * 3 + 1) + "' " + prefix + "1='"
                        + headName + "' " + prefix + "1Align='Center' " + prefix + "1Type='Html' ");
                for (int j = 1; j <= studyYear; j++) {
                    String stu_prefix = prefix + j;
                    header2.append(stu_prefix + "='Yr-" + j + "' " + stu_prefix + "Align='Center' "
                            + stu_prefix + "Type='Html' ");
                    header2.append(stu_prefix + "_actual='Actual&lt;br/&gt;Enrollment' "
                            + stu_prefix + "_actualAlign='Center' " + stu_prefix
                            + "_actualType='Html' ");

                    header2.append(stu_prefix + "_break='Yr-" + j + " (Break Down)'  " + stu_prefix
                            + "_breakAlign='Center' " + stu_prefix + "_breakType='Html' ");

                }
                header2.append(prefix + (studyYear * 3 + 1) + "='Total' " + prefix
                        + (studyYear * 3 + 1) + "Align='Center' " + prefix + (studyYear * 3 + 1)
                        + "Type='Html' ");
            } else {
                header1.append(prefix + "1Span='" + (studyYear * 2 + 1) + "' " + prefix + "1='"
                        + headName + "' " + prefix + "1Align='Center' " + prefix + "1Type='Html' ");
                for (int j = 1; j <= studyYear; j++) {
                    String stu_prefix = prefix + j;
                    header2.append(stu_prefix + "='Yr-" + j + "' " + stu_prefix + "Align='Center' "
                            + stu_prefix + "Type='Html' ");

                    header2.append(stu_prefix + "_break='Yr-" + j + " (Break Down)'  " + stu_prefix
                            + "_breakAlign='Center' " + stu_prefix + "_breakType='Html' ");

                }
                header2.append(prefix + (studyYear * 2 + 1) + "='Total' " + prefix
                        + (studyYear * 2 + 1) + "Align='Center' " + prefix + (studyYear * 2 + 1)
                        + "Type='Html' ");
            }
        }
        header1.append("/>");
        header2.append("/>");

        String head = "<Head>" + header1.toString() + header2.toString() + "</Head>";
        log.debug("Head ={}", head);
        gridDoc.append(head);

    }

    private void generateViewDef(final StringBuffer gridDoc, final Integer level2Start,
            final Integer level2End, final int studyYear, final Integer exerciseYear,
            final Map<String, String> fundingModeMap) {
        StringBuffer treeSb = new StringBuffer();
        StringBuffer leafSb = new StringBuffer();

        treeSb.append("<D Name='Tree' Calculated='1' ");
        leafSb.append("<D Name='Leaf' Calculated='1' ");

        for (Integer i = level2Start; i < level2End + 1; i++) {
            String prefix = "_" + i + "_";
            String tempName = "";
            if (i < exerciseYear) {
                for (int j = 1; j <= studyYear; j++) {
                    treeSb.append(prefix + j + "Formula='sum()' ");
                    treeSb.append(prefix + j + "_actualFormula='sum()' ");
                    if (j == 1) {
                        tempName = prefix + j;
                    } else {
                        tempName += "+" + prefix + j;
                    }
                }
                treeSb.append(prefix + (studyYear * 2 + 1) + "Formula='sum()' ");
                leafSb.append(prefix + (studyYear * 2 + 1) + "Formula='" + tempName + "' ");
            } else if (i.equals(exerciseYear)) {
                for (int j = 1; j <= studyYear; j++) {
                    treeSb.append(prefix + j + "Formula='sum()' ");
                    treeSb.append(prefix + j + "_actualFormula='sum()' ");
                    if (j == 1) {
                        tempName = prefix + j;
                    } else {
                        tempName += "+" + prefix + j;
                    }
                }
                treeSb.append(prefix + (studyYear * 3 + 1) + "Formula='sum()' ");
                leafSb.append(prefix + (studyYear * 3 + 1) + "Formula='" + tempName + "' ");
            } else {
                for (int j = 1; j <= studyYear; j++) {
                    treeSb.append(prefix + j + "Formula='sum()' ");
                    if (j == 1) {
                        tempName = prefix + j;
                    } else {
                        tempName += "+" + prefix + j;
                    }
                }
                treeSb.append(prefix + (studyYear * 2 + 1) + "Formula='sum()' ");
                leafSb.append(prefix + (studyYear * 2 + 1) + "Formula='" + tempName + "' ");
            }
        }

        treeSb.append("/>");
        leafSb.append("/>");

        String fmDef = generateFMDef(level2Start, level2End, studyYear, exerciseYear,
                fundingModeMap);

        String def = "<Def>" + treeSb.toString() + leafSb.toString() + fmDef + "</Def>";
        log.debug("Def ={}", def);
        gridDoc.append(def);
    }

    private String generateFMDef(final Integer level2Start, final Integer level2End,
            final int studyYear, final Integer exerciseYear,
            final Map<String, String> fundingModeMap) {
        if (fundingModeMap.isEmpty()) {
            return "";
        }

        StringBuffer sb = new StringBuffer();
        Set<String> fmSet = fundingModeMap.keySet();
        for (String fm : fmSet) {
            sb.append("<D Name='FM_" + fm + "' Calculated='1' ");
            String fm_dec = fm;

            for (Integer i = level2Start; i < level2End + 1; i++) {
                String prefix = "_" + i + "_";
                log.info("i :{}", i);
                if (i <= exerciseYear) {

                    for (int j = 1; j <= studyYear; j++) {
                        String temp = prefix + j;
                        String tema = prefix + j + "_actual";

                        sb.append(temp + "Formula='sum(&#039;" + temp + "&#039;,&#039;A==&quot;"
                                + fm_dec + "&quot;&#039;,4)'  ");
                        sb.append(tema + "Formula='sum(&#039;" + tema + "&#039;,&#039;A==&quot;"
                                + fm_dec + "&quot;&#039;,4)' ");
                    }
                } else {
                    for (int j = 1; j <= studyYear; j++) {
                        sb.append(prefix + j + "Formula='sum(&#039;" + prefix + j
                                + "&#039;,&#039;A==&quot;" + fm_dec + "&quot;&#039;,4)' ");
                    }
                }
            }
            sb.append("/>");
        }
        return sb.toString();
    }

    private void generateViewCols(final StringBuffer gridDoc, final int studyYear,
            final Integer level2Start, final Integer level2End, final Integer exerciseYear) {
        StringBuffer sb = new StringBuffer();
        sb.append("<Cols>");

        for (Integer i = level2Start; i < level2End + 1; i++) {
            String prefix = "_" + i + "_";
            if (i < exerciseYear) {
                for (int j = 1; j <= studyYear; j++) {
                    sb.append("<C Name='" + prefix + j
                            + "' Width='90' Type='Int' Align='Center' CanEdit='0' />");
                    sb.append("<C Name='" + prefix + j
                            + "_actual' Width='90' Type='Int' Align='Center' CanEdit='0'/>");
                }
                sb.append("<C Name='" + prefix + (studyYear * 2 + 1)
                        + "' Width='90' Type='Float' Align='Center'/>");
            } else if (i.equals(exerciseYear)) {
                for (int j = 1; j <= studyYear; j++) {
                    sb.append("<C Name='" + prefix + j
                            + "' Width='90' Type='Int' Align='Center' CanEdit='1' />");
                    sb.append("<C Name='" + prefix + j
                            + "_actual' Width='90' Type='Int' Align='Center' CanEdit='0' />");
                    sb.append("<C Name='" + prefix + j
                            + "_break' Width='90' Type='Html' Align='Center' CanEdit='0' />");
                }
                sb.append("<C Name='" + prefix + (studyYear * 3 + 1)
                        + "' Width='90' Type='Float' Align='Center'/>");
            } else {
                for (int j = 1; j <= studyYear; j++) {
                    sb.append("<C Name='" + prefix + j + "' Width='90' Type='Int' Align='Center'/>");
                    sb.append("<C Name='" + prefix + j
                            + "_break' Width='90' Type='Html' Align='Center' CanEdit='0' />");
                }
                sb.append("<C Name='" + prefix + (studyYear * 2 + 1)
                        + "' Width='90' Type='Float' Align='Center'/>");
            }
        }
        sb.append("</Cols>");

        log.debug("Cols ={}", sb.toString());
        gridDoc.append(sb.toString());
    }

    private void generateFoot(final StringBuffer gridDoc, final int studyYear,
            final Integer level2Start, final Integer level2End, final Integer exerciseYear,
            final Map<String, String> fundingModeMap) {

        StringBuffer sb = new StringBuffer();

        sb.append("<Lang><Format RangeSeparator='~'  /></Lang><Foot>");
        sb.append("<I Def='Tree' A='&lt;B&gt;Total&lt;/B&gt;' AType='Html' CanEdit='0' />");
        if (fundingModeMap.isEmpty()) {
            sb.append("</Foot>");
            gridDoc.append(sb.toString());
            return;
        }
        StringBuffer fm_sb = new StringBuffer();
        // StringBuffer bur_sb = new StringBuffer();
        fm_sb.append("<I A='By Funding Mode :' AType='Text' CanEdit='0' ");
        // bur_sb.append("<I A='By Bureau :' AType='Text' CanEdit='0' ");

        StringBuffer temp = new StringBuffer();
        StringBuffer forFM = new StringBuffer();
        for (Integer i = level2Start; i <= level2End; i++) {
            String prefix = "_" + i + "_";
            if (i < exerciseYear) {
                for (int j = 1; j <= studyYear; j++) {
                    temp.append(prefix + j + "Type='Text'  " + prefix + j + "_actualType='Text'  ");

                    // if (j == 1) {
                    // tempName = prefix + j + "+" + prefix + j + "_actual";
                    // } else {
                    // tempName += "+" + prefix + j + "+" + prefix + j +
                    // "_actual";
                    // }
                }
                temp.append(prefix + (studyYear * 2 + 1) + "Type='Text'  " + prefix
                        + (studyYear * 2 + 1) + "CanEdit='0'  ");
                forFM.append(prefix + (studyYear * 2 + 1) + "Type='Text'  " + prefix
                        + (studyYear * 2 + 1) + "CanEdit='0'  ");
                // temp.append(prefix + (studyYear * 2 + 1) +
                // "Formula='sum()' ");
                // temp.append(prefix + (studyYear * 2 + 1) + "Formula='" +
                // tempName + "' ");
            } else if (i.equals(exerciseYear)) {
                for (int j = 1; j <= studyYear; j++) {
                    temp.append(prefix + j + "Type='Text'  " + prefix + j + "_actualType='Text'  "
                            + prefix + j + "_breakType='Text'  ");

                    // if (j == 1) {
                    // tempName = prefix + j + "+" + prefix + j + "_actual";
                    // } else {
                    // tempName += "+" + prefix + j + "+" + prefix + j +
                    // "_actual";
                    // }
                }
                temp.append(prefix + (studyYear * 3 + 1) + "Type='Text'  " + prefix
                        + (studyYear * 3 + 1) + "CanEdit='0'  ");
                forFM.append(prefix + (studyYear * 3 + 1) + "Type='Text'  " + prefix
                        + (studyYear * 3 + 1) + "CanEdit='0'  ");
                // temp.append(prefix + (studyYear * 3 + 1) +
                // "Formula='sum()' ");
                // temp.append(prefix + (studyYear * 3 + 1) + "Formula='" +
                // tempName + "' ");
            } else {
                for (int j = 1; j <= studyYear; j++) {
                    temp.append(prefix + j + "Type='Text' " + prefix + j + "_breakType='Text'   ");
                    // if (j == 1) {
                    // tempName = prefix + j;
                    // } else {
                    // tempName += "+" + prefix + j;
                    // }
                }
                temp.append(prefix + (studyYear * 2 + 1) + "Type='Text'  " + prefix
                        + (studyYear * 2 + 1) + "CanEdit='0'  ");
                forFM.append(prefix + (studyYear * 2 + 1) + "Type='Text'  " + prefix
                        + (studyYear * 2 + 1) + "CanEdit='0'  ");
                // temp.append(prefix + (studyYear * 2 + 1) +
                // "Formula='sum()' ");
                // temp.append(prefix + (studyYear * 2 + 1) + "Formula='" +
                // tempName + "' ");
            }
        }

        temp.append("/>");
        fm_sb.append(temp);
        // bur_sb.append(temp);
        //
        Set<String> fmSet = fundingModeMap.keySet();
        for (String fm : fmSet) {
            fm_sb.append("<I Def='FM_" + fm + "' A='" + fm + "' " + forFM.toString() + "/>");
        }
        sb.append(fm_sb);
        sb.append("</Foot>");

        log.debug("Foot ={}", sb.toString());
        gridDoc.append(sb.toString());
    }

    private void generateViewLeftCols(final StringBuffer gridDoc, final boolean isEndorsed,
            final boolean isForEndorsement) {
        StringBuffer sb = new StringBuffer();
        sb.append("<LeftCols>");
        sb.append("<C Name='A' Width='190' Type='Text' CanEdit='0'  AVisible='0' />");
        sb.append("<C Name='B' Width='60' Type='Text' CanEdit='0' Align='Center'  BVisible='0' />");
        sb.append("<C Name='C' Width='60' Type='Text' CanEdit='0' Align='Center'  CVisible='0' />");
        sb.append("<C Name='D' Width='60' Type='Text' CanEdit='0' Align='Center'  DVisible='0' />");
        sb.append("<C Name='E' Width='60' Type='Text' CanEdit='0' Align='Center'  EVisible='0' />");
        sb.append("<C Name='F' Width='60' Type='Text' CanEdit='0' Align='Center'  FVisible='0' />");
        if (isForEndorsement) {
            sb.append("<C Name='G' Width='60' Type='Text' CanEdit='0' Align='Center'  GVisible='0' />");
            sb.append("<C Name='H' Width='60' Type='Bool' CanEdit='0' Align='Center'  HVisible='0' />");
            sb.append("<C Name='K' Width='60' Type='Lines' CanEdit='0' Align='Center'  KVisible='0' />");
        } else if (isEndorsed) {
            sb.append("<C Name='G' Width='60' Type='Text' CanEdit='0' Align='Center' GVisible='0' />");
        }

        sb.append("</LeftCols>");
        log.debug("LeftCols ={}", sb.toString());
        gridDoc.append(sb.toString());
    }

    @Override
    public void saveViewDetail(final String viewDetailData) throws Exception {
        log.info("XML = {}", viewDetailData);
        Iterator<Element> ite;
        try {
            ite = GridDoc.parseChanges(viewDetailData);
        } catch (DocumentException e) {
            log.error(e.getMessage(), e);
            return;
        }
        while (ite.hasNext()) {
            Element I = ite.next();
            if (!StringUtils.equals("1", I.attributeValue("Changed"))) {
                continue;
            }

            String id = I.attributeValue("id");
            log.info("id: {}", id);
            if (StringUtils.isBlank(id)) {
                continue;
            }

            // discipline
            String[] ids = id.split("_");
            String level2PlanningId = ids[0];
            if (id.indexOf("reject") != -1) {
                saveDetailForEndorsement(I, ids, level2PlanningId);
            } else if (id.indexOf("New") != -1 || id.indexOf("Prog") != -1) {
                saveDetailForPlanning(I, ids, level2PlanningId);
            }
        }
    }

    private void saveDetailForEndorsement(final Element I, final String[] ids,
            final String level2PlanningId) throws Exception {
        String categoryNodeId = ids[1];
        String discipline = ids[2];
        Iterator<Attribute> attrs = I.attributeIterator();
        while (attrs.hasNext()) {
            Attribute attr = attrs.next();
            String cellName = attr.getName();
            String cellvalue = attr.getValue();
            log.info("cellvalue : {}", cellvalue);
            if (cellName.equals("H")) {
                Level2DisciplineStatus l2s = level2DisciplineStatusService.findDisciplineStatus(
                        level2PlanningId, discipline, categoryNodeId);
                l2s.setStatus(PlanningStatus.Rejected);
                level2DisciplineStatusService.save(l2s);

                Level2Planning level2Planning = findById(level2PlanningId);
                Planning planning = level2Planning.getPlanning();
                planning.setStatus(PlanningStatus.Rejected);
                planningService.save(planning);

                CategoryLeaf node = categoryNodeService.findLeafById(categoryNodeId);
                node.setLevel2status(PlanningStatus.Rejected);
                categoryNodeService.saveLeaf(node);

                // sendMail(EmailNotificationType.Level2Reject, node.getName(),
                // discipline);

            } else if (cellName.equals("K")) {
                Level2DisciplineStatus l2s = level2DisciplineStatusService.findDisciplineStatus(
                        level2PlanningId, discipline, categoryNodeId);
                l2s.setReason(cellvalue);
                level2DisciplineStatusService.save(l2s);
            }
        }
    }

    // TODO
    private void sendMail(final EmailNotificationType type, final String nodeName,
            final String discipline) throws Exception {

        // String emailType = EmailNotificationType.Level2Reject.name();
        //
        // List<EmailTemplate> templateList =
        // emailTemplateManager.findByEmailNotificationType(type);
        // if (templateList.isEmpty())
        // return;
        // EmailTemplate tempalte = templateList.get(0);
        //
        // List<EmailSetting> emailList =
        // emailSettingManager.findByEmailNotificationType(type);
        // if (emailList.isEmpty())
        // return;
        //
        // EmailSetting setting = emailList.get(0);

        Map<String, String> values = new HashMap<String, String>();
        values.put("leafNode", nodeName);
        values.put("disciplineName", discipline);

        String from = "jyang@accentrix.com";
        String[] tos = { "jyang@accentrix.com,czhang@accentrix.com" };

        emailService.sendEmail(type.toString(), "Email Notifycation", values, from, tos);
    }

    private void saveDetailForPlanning(final Element I, final String[] ids,
            final String level2PlanningId) throws Exception {
        String code = ids[1];
        String operatingCampus = ids[2];
        String operatingUnit = ids[3];
        String type = ids[4];
        Iterator<Attribute> attrs = I.attributeIterator();
        while (attrs.hasNext()) {
            Attribute attr = attrs.next();
            String cellName = attr.getName();
            // if (!StringUtils.startsWith(cellName, "_")) {
            if (!cellName.startsWith("_")) {
                continue;
            }
            String cellvalue = attr.getValue();
            Integer cellvalueInt = Integer.parseInt(cellvalue);
            String planYear = cellName.substring(1, cellName.indexOf("_", 1));
            String studyYear = "";
            if (cellName.indexOf("actual") != -1) {
                studyYear = cellName.substring(cellName.lastIndexOf("_") - 1,
                        cellName.lastIndexOf("_"));
            } else {
                studyYear = cellName.substring(cellName.length() - 1, cellName.length());
            }
            log.info("cellvalue : {}", cellvalue);
            log.info("level2PlanningId : {}", level2PlanningId);
            log.info("code : {}", code);
            log.info("operatingCampus : {}", operatingCampus);
            log.info("operatingUtil : {}", operatingUnit);
            log.info("planYear : {}", planYear);
            log.info("studyYear : {}", studyYear);
            ProgrammePlanningItem ppi = programmePlanningItemService.findUniquePpi(
                    level2PlanningId, code, Integer.valueOf(planYear), Integer.valueOf(studyYear),
                    operatingCampus, operatingUnit);
            if (type.equals("New")) {
                if (cellName.indexOf("actual") != -1) {
                    ppi.setPlannedPlacesNewActual(cellvalueInt);
                } else {
                    ppi.setPlannedPlacesNew(cellvalueInt);
                }
            } else {
                if (cellName.indexOf("actual") != -1) {
                    ppi.setPlannedPlacesProgActual(cellvalueInt);
                } else {
                    ppi.setPlannedPlacesProg(cellvalueInt);
                }
            }
            programmePlanningItemService.save(ppi);
        }
    }

    @Override
    public Level2Planning findByPlanningId(final String planningId) {
        List<Level2Planning> level2Plannings = level2PlanningDao.findBy("planning.id", planningId);
        if (level2Plannings != null && level2Plannings.size() != 0) {
            return level2Plannings.get(0);
        }
        return null;
    }

    @Override
    public void endorseLevel2(final String level2PlanningId) throws Exception {
        Level2Planning level2Planning = findById(level2PlanningId);
        List<Level2DisciplineStatus> list = level2Planning.getLevel2DisciplineStatusList();
        boolean hasReject = false;
        for (Level2DisciplineStatus ds : list) {
            if (ds.getStatus().equals(PlanningStatus.Rejected)) {
                hasReject = true;
                break;
            }
        }
        if (hasReject) {
            throw new Exception(
                    " Some Discipline Status's status is reject, so you cann't endorse the Planning! ");
        } else {
            Planning planning = level2Planning.getPlanning();
            planning.setStatus(PlanningStatus.Endorsed);
            planningService.save(planning);

            throw new Exception(" Endorse this Planning successfully!");
        }

    }

    @Override
    public void confirmDetail(final String level2PlanningId, final String categoryNodeId)
            throws Exception {
        Level2Planning level2 = get(level2PlanningId);
        Planning planning2 = level2.getPlanning();

        CategoryNode node = categoryNodeService.findById(categoryNodeId);
        node.setLevel2status(PlanningStatus.WaitingForEndorsement);
        categoryNodeService.saveLeaf((CategoryLeaf) node);

        List<Level2DisciplineStatus> statusList = level2DisciplineStatusService
                .findByLevel2IdAndNodeid(level2PlanningId, categoryNodeId);
        if (!statusList.isEmpty()) {
            for (Level2DisciplineStatus status : statusList) {
                if (status.getStatus().equals(PlanningStatus.WaitingForEndorsement)) {
                    continue;
                } else {
                    status.setStatus(PlanningStatus.WaitingForEndorsement);
                    level2DisciplineStatusService.save(status);
                }
            }
            if (level2DisciplineStatusService
                    .checkAllDisciplineIsNotRejectByLevel2Id(level2PlanningId)) {
                planning2.setStatus(PlanningStatus.WaitingForEndorsement);
                planningService.save(planning2);
            }
        } else {
            if (checkTreeWithLevel2Status(node)) {
                planning2.setStatus(PlanningStatus.WaitingForEndorsement);
                planningService.save(planning2);
            }
        }

    }

    private boolean checkTreeWithLevel2Status(final CategoryNode node) {
        if (node.getChildren().isEmpty()) {
            if (node.getIsLeafNode() == 1) {
                if (!node.getLevel2status().equals(PlanningStatus.WaitingForEndorsement)) {
                    return false;
                }
            }
        } else {
            for (CategoryNode child : node.getChildren()) {
                return checkTreeWithLevel2Status(child);
            }
        }
        return true;
    }

    //
    // private void changeNodeStatus(final CategoryNode categoryNode, final
    // String level2PlanningId,
    // final String categoryId) throws Exception {
    // List<CategoryNode> children = categoryNode.getChildren();
    // if (categoryNode.getChildren().isEmpty()) {
    // categoryNode.setLevel2status(PlanningStatus.WaitingForEndorsement);
    // if (categoryNode.getIsLeafNode() == 1) {
    // categoryNodeService.saveLeaf((CategoryLeaf) categoryNode, categoryId);
    // changeDisciplineStatus(level2PlanningId, categoryNode.getId());
    // } else {
    // categoryNodeService.saveBranch((CategoryBranch) categoryNode,
    // categoryId);
    // }
    // } else {
    // for (int i = 0; i < children.size(); i++) {
    // CategoryNode child = children.get(i);
    // changeNodeStatus(child, level2PlanningId, categoryId);
    // }
    // }
    // }
    //
    @Override
    public void generalDefaultValue(final Level2Planning level2Planning) throws Exception {
        Category category = level2Planning.getPlanning().getCategory();
        int exerciseYear = category.getPlanYear();

        List<String> programmes = programmeManager.distinctCodeByLaunchYear(exerciseYear);
        List<Programme> progList = filterProgramme(exerciseYear, programmes, null, null);

        log.info("level2Planning.getId() : {}", level2Planning.getId());

        for (Programme programme : progList) {
            if (programme.isRequireStream()) {
                log.info("Stream : ");
                // stream
                if (programme.getDuration() == 1) {
                    continue;
                }
                List<Stream> streamList = programme.getStreams();
                for (Stream stream : streamList) {
                    generalStreamDBData(level2Planning, exerciseYear, stream,
                            programme.getDuration());
                }
            } else {
                // subjectGroup
                log.info("subjectGroup code : {} ", programme.getCode());
                ProgrammeCode programmeCode = codeManager.getByCode(programme.getCode());
                log.info("programmeCode : {}", programmeCode.getCode());

                List<SubjectGroup> sgList = programmeCode.getSubjectGroups();
                if (!sgList.isEmpty()) {
                    log.info("SubjectGroup : ");
                    int year = 0;
                    Map<Integer, SubjectGroup> map = new HashMap<Integer, SubjectGroup>();
                    for (SubjectGroup sg : sgList) {
                        int sgYear = sg.getPlanningExercise();
                        if (sgYear <= exerciseYear) {
                            if (sgYear > year) {
                                year = sgYear;
                                map.put(year, sg);
                            }
                        }
                    }
                    if (year == 0) {
                        continue;
                    }
                    SubjectGroup sg = map.get(year);
                    List<Programme> feeders = programmeManager.findByCodeAndCurrentYear(sg
                            .getProgrammeCode().getCode(), sg.getPlanningExercise());
                    if (feeders.isEmpty()) {
                        continue;
                    }

                    Programme feeder = feeders.get(0);
                    List<SubjectGroupItem> subjectGroupItems = sg.getSubjectGroupItems();
                    if (subjectGroupItems.isEmpty()) {
                        continue;
                    }
                    String[] codes = new String[subjectGroupItems.size()];
                    for (int i = 0; i < subjectGroupItems.size(); i++) {
                        SubjectGroupItem sgi = subjectGroupItems.get(i);
                        codes[i] = sgi.getProgrammeCode().getCode();
                    }
                    List<Programme> subItemList = programmeManager.findByCodesAndLaunchYear(codes,
                            exerciseYear);
                    for (Programme prog : subItemList) {
                        generalDBData(level2Planning, exerciseYear, prog, feeder);
                    }
                } else {
                    log.info("normal : ");
                    // normal
                    if (programme.getDuration() == 1) {
                        continue;
                    }
                    generalDBData(level2Planning, exerciseYear, programme, null);
                }
            }
        }

    }

    private List<Programme> filterProgramme(final int exerciseYear, final List<String> codes,
            final String disciplilneType, final String searchInService) {
        List<Programme> progList = new ArrayList<Programme>();
        for (String str : codes) {
            log.info("filterProgramme code : {}", str);
            List<Programme> temp = programmeManager.findByCodeAndCurrentYear(str, exerciseYear);
            if (temp.isEmpty()) {
                List<Programme> temp2 = programmeManager.findByCodeAndLaunchYear(str, exerciseYear);

                int max = -1;
                int maxSeason = -1;
                Programme maxProgramme = null;
                for (Programme pro : temp2) {

                    String launchVersion = pro.getLaunchVersion();
                    if (launchVersion.startsWith("M")) {
                        int mod = (Integer.valueOf(launchVersion.substring(1)) + 4) % 13;
                        if (mod > max) {
                            max = mod;
                            maxProgramme = pro;
                        }
                    } else {

                        if ("sum".equalsIgnoreCase(launchVersion)) {
                            maxSeason = 4;
                            maxProgramme = pro;
                        }
                        if ("spr".equalsIgnoreCase(launchVersion)) {
                            if (maxSeason < 3) {
                                maxSeason = 3;
                                maxProgramme = pro;
                            }
                        }
                        if ("win".equalsIgnoreCase(launchVersion)) {
                            if (maxSeason < 2) {
                                maxSeason = 2;
                                maxProgramme = pro;
                            }
                        }
                        if ("aut".equalsIgnoreCase(launchVersion)) {
                            if (maxSeason < 1) {
                                maxSeason = 1;
                                maxProgramme = pro;
                            }
                        }

                    }
                }
                // maxProgramme为最大值
                // end
                // TODO
                if (maxProgramme != null) {
                    log.info(
                            "maxProgramme : {}",
                            maxProgramme.getCode() + " " + maxProgramme.getLaunchYear() + " "
                                    + maxProgramme.getLaunchVersion() + " "
                                    + maxProgramme.getDiscipline());
                    if (StringUtils.isNotBlank(disciplilneType)) {
                        if (!maxProgramme.getDiscipline().equals(disciplilneType)) {
                            continue;
                        }
                    }

                    if (StringUtils.isNotBlank(searchInService)) {
                        if (!maxProgramme.getPreEmploymentInService().equals(searchInService)) {
                            continue;
                        }
                    }
                    progList.add(maxProgramme);
                }
            } else {
                if (StringUtils.isNotBlank(searchInService)) {
                    for (Programme pro : temp) {
                        if (!pro.getPreEmploymentInService().equals(searchInService)) {
                            continue;
                        } else {
                            progList.add(pro);
                        }
                    }
                } else {
                    progList.addAll(temp);
                }
            }
        }
        return progList;
    }

    private List<Programme> filterTreeProgramme(final int exerciseYear,
            final List<Programme> progList) {
        // for (Programme programme : progList) {
        for (int i = 0; i < progList.size(); i++) {
            Programme programme = progList.get(i);
            if (programme.getLaunchYear() == exerciseYear)
                continue;

            List<Programme> temp2 = programmeManager.findByCodeAndLaunchYear(programme.getCode(),
                    exerciseYear);

            int max = -1;
            int maxSeason = -1;
            Programme maxProgramme = null;
            for (Programme pro : temp2) {

                String launchVersion = pro.getLaunchVersion();
                if (launchVersion.startsWith("M")) {
                    int mod = (Integer.valueOf(launchVersion.substring(1)) + 4) % 13;
                    if (mod > max) {
                        max = mod;
                        maxProgramme = pro;
                    }
                } else {

                    if ("sum".equalsIgnoreCase(launchVersion)) {
                        maxSeason = 4;
                        maxProgramme = pro;
                    }
                    if ("spr".equalsIgnoreCase(launchVersion)) {
                        if (maxSeason < 3) {
                            maxSeason = 3;
                            maxProgramme = pro;
                        }
                    }
                    if ("win".equalsIgnoreCase(launchVersion)) {
                        if (maxSeason < 2) {
                            maxSeason = 2;
                            maxProgramme = pro;
                        }
                    }
                    if ("aut".equalsIgnoreCase(launchVersion)) {
                        if (maxSeason < 1) {
                            maxSeason = 1;
                            maxProgramme = pro;
                        }
                    }

                }
            }
            // maxProgramme为最大值
            // end
            // TODO
            if (maxProgramme != null) {
                if (progList.contains(maxProgramme)) {
                    temp2.remove(maxProgramme);
                }
                for (Programme pro : temp2) {
                    if (progList.contains(pro)) {
                        progList.remove(pro);
                    }
                }
            }
        }
        return progList;
    }

    private void generalDBData(final Level2Planning level2Planning, final int exerciseYear,
            final Programme programme, final BaseProgramme subjectGroupFeeder) throws Exception {
        log.info("programme id : {}", programme.getId());
        ProgrammePlanProjectionType projectionType = null;
        List<OfferingCampus> ocampusList = programme.getOfferingCampusList();

        for (OfferingCampus oc : ocampusList) {

            log.info("oc.getOperatingCampus() : {}", oc.getOperatingCampus());
            log.info("oc.getOperatingUnit() : {}", oc.getOperatingUnit());
            log.info("getDuration() : {}", programme.getDuration());

            for (int i = 1; i < programme.getDuration(); i++) {

                if (subjectGroupFeeder != null) {
                    if (i == 1) {
                        projectionType = projectionTypeManager
                                .findByProgrammeIdAndSubjectGroupIdAndYear(
                                        subjectGroupFeeder.getId(), programme.getId(), i);
                    } else {
                        projectionType = projectionTypeManager.findByProgrammeAndYearWithoutSub(
                                programme, i);
                    }
                } else {
                    projectionType = projectionTypeManager.findByProgrammeAndYearWithoutSub(
                            programme, i);
                }
                if (projectionType == null) {
                    return;
                }

                calculateData(level2Planning, exerciseYear, programme.getCode(),
                        level2Planning.getId(), oc, i, projectionType);
            }
        }
    }

    private void generalStreamDBData(final Level2Planning level2Planning, final int exerciseYear,
            final Stream stream, final int duration) throws Exception {

        String code = stream.getStreamCode();
        String level2PlanningId = level2Planning.getId();

        log.info("stream id : {}", stream.getId());
        List<OfferingCampus> ocampusList = stream.getOfferingCampusList();

        for (OfferingCampus oc : ocampusList) {

            log.info("oc.getOperatingCampus() : {}", oc.getOperatingCampus());
            log.info("oc.getOperatingUnit() : {}", oc.getOperatingUnit());
            if (oc.getOperatingCampus().equals("E2") && oc.getOperatingUnit().equals("DE")) {
                log.info("oc.getOperatingUnit() : {}", oc.getOperatingUnit());
            }

            for (int i = 1; i < duration; i++) {

                ProgrammePlanProjectionType projectionType = projectionTypeManager
                        .findByProgrammeAndYearWithoutSub(stream, i);
                if (projectionType == null) {
                    return;
                }

                calculateData(level2Planning, exerciseYear, stream.getCode() + code,
                        level2PlanningId, oc, i, projectionType);
            }
        }
    }

    private void calculateData(final Level2Planning level2Planning, final int exerciseYear,
            final String code, final String level2PlanningId, final OfferingCampus oc, final int i,
            final ProgrammePlanProjectionType projectionType) throws Exception {

        log.info("projectionType : =-----------{}", projectionType.getPlanProjectionType());

        if (projectionType.getPlanProjectionType() == null) {
            return;
        }

        ProgrammePlanningItem ppi = programmePlanningItemService.findUniquePpi(level2PlanningId,
                code, exerciseYear, i, oc.getOperatingCampus(), oc.getOperatingUnit());
        if (ppi == null) {
            ppi = new ProgrammePlanningItem();
        }
        ppi.setLevel2Planning(level2Planning);
        ppi.setCode(code);
        ppi.setOperatingCampus(oc.getOperatingCampus());
        ppi.setOperatingUnit(oc.getOperatingUnit());
        ppi.setPlanYear(exerciseYear);
        ppi.setStudyYear(i);
        ppi.setPlannedPlacesNew(0);

        if (projectionType.getPlanProjectionType().equals(PlanProjectionType.DirectInput)) {
            ppi.setPlannedPlacesProg(0);
        } else if (projectionType.getPlanProjectionType().equals(
                PlanProjectionType.SelfDefinedProgressionRate)) {
            if (i == 1) {
                ppi.setPlannedPlacesProg(0);
            } else {
                ProgrammePlanningItem lastYear = programmePlanningItemService.findUniquePpi(
                        level2PlanningId, code, exerciseYear - 1, i - 1, oc.getOperatingCampus(),
                        oc.getOperatingUnit());
                int value = 0;
                if (lastYear != null) {
                    value = (int) ((lastYear.getPlannedPlacesNew() + lastYear
                            .getPlannedPlacesProg())
                            * projectionType.getSelfDefinedProgressionRate() / 100 + 0.5);
                }
                ppi.setPlannedPlacesProg(value);
                log.info("value : =-----------{}", value);
            }
        } else if (projectionType.getPlanProjectionType().equals(PlanProjectionType.PastEnrolment)) {
            double all = 0.0;
            int allYear = 0;
            for (int j = exerciseYear - 1; j >= exerciseYear - 5; j--) {

                ProgrammePlanningItem old_1 = programmePlanningItemService.findUniquePpi(
                        level2PlanningId, code, j, i + 1, oc.getOperatingCampus(),
                        oc.getOperatingUnit());
                if (old_1 == null) {
                    continue;
                }
                double progA = old_1.getPlannedPlacesProgActual();
                if (progA == 0) {
                    progA = old_1.getPlannedPlacesProg();
                }
                ProgrammePlanningItem old_2 = programmePlanningItemService.findUniquePpi(
                        level2PlanningId, code, j - 1, i, oc.getOperatingCampus(),
                        oc.getOperatingUnit());
                if (old_2 == null) {
                    continue;
                }
                double progA_2 = old_2.getPlannedPlacesProgActual();
                if (progA_2 == 0) {
                    progA_2 = (old_2.getPlannedPlacesProg() + old_2.getPlannedPlacesNew());
                }
                log.info("progA : , progA_2 : {}", progA + ", " + progA_2);
                all += progA / progA_2;
                log.info("rate : {}", progA / progA_2);
                ++allYear;
            }
            log.info("all : {}", all);
            log.info("allYear : {}", allYear);
            int value = 0;
            if (allYear != 0 && (int) all != 0) {

                ProgrammePlanningItem lastYear = programmePlanningItemService.findUniquePpi(
                        level2PlanningId, code, exerciseYear - 1, i, oc.getOperatingCampus(),
                        oc.getOperatingUnit());
                log.info("lastYear.getPlannedPlacesNew() : {}", lastYear.getPlannedPlacesNew());
                value = (int) (all / allYear * (lastYear.getPlannedPlacesNew().intValue()) + 0.5);
                log.info("value : {}", value);
            }
            ppi.setPlannedPlacesProg(value);
        }
        // TODO actual enrollment from system
        ppi.setPlannedPlacesNewActual(0);
        ppi.setPlannedPlacesProgActual(0);
        programmePlanningItemService.save(ppi);
    }

    // <bdo Debug='3'
    // Data_Url="plan-second!loadLevel2TreeByLevel1Status.action?no-decorate&level2Planning.id=<s:property value='level2Planning.id'/>&programmeCode=<s:property value='programmeCode' />"
    // >
    // </bdo>
}
